/* picoc data type module. This manages a tree of data types and has facilities
 * for parsing data types. */

#include "type.h"
#include "lex.h"
#include "table.h"
#include "variable.h"
#include "platform.h"
#include "parse.h"
#include "expression.h"
int Type::pointerAlignBytes = 0;
int Type::intAlignBytes = 0;
/* add a new type to the set of types we know about */
ValueType *Type::TypeAdd(Interpreter *pc, ParseState *parser,
    ValueType *parentType, BaseType base, int arraySize,
    const char *identifier, int sizeNeed, int alignBytes) {
    ValueType *newType = (ValueType *)Variable::VariableAlloc(pc, parser,
        sizeof(ValueType), true);
    newType->base = base;
    newType->arraySize = arraySize;
    newType->sizeNeed = sizeNeed;
    newType->alignBytes = alignBytes;
    newType->identifier = identifier;
    newType->members = NULL;
    newType->fromType = parentType;
    newType->derivedTypeList = NULL;
    newType->onHeap = true;
    newType->next = parentType->derivedTypeList;
    parentType->derivedTypeList = newType;

    return newType;
}

/* given a parent type, get a matching derived type and make one if necessary.
 * identifier should be registered with the shared string table. */
ValueType *Type::TypeGetMatching(Interpreter *pc, ParseState *parser,
    ValueType *parentType, BaseType base, int arraySize,
    const char *identifier, int allowDuplicates) {
    int sizeNeed;
    int alignBytes;
    ValueType *thisType = parentType->derivedTypeList;
    while (thisType != NULL && (thisType->base != base ||
            thisType->arraySize != arraySize || thisType->identifier != identifier))
        thisType = thisType->next;

    if (thisType != NULL) {
        if (allowDuplicates)
            return thisType;
        else
            Platform::ProgramFail(parser, "data type '%s' is already defined", identifier);
    }

    switch (base) {
    case kTypePointer:
        sizeNeed = sizeof(void*);
        alignBytes = pointerAlignBytes;
        break;
    case kTypeArray:
        sizeNeed = arraySize * parentType->sizeNeed;
        alignBytes = parentType->alignBytes;
        break;
    case kTypeEnum:
        sizeNeed = sizeof(int);
        alignBytes = intAlignBytes;
        break;
    default:
        sizeNeed = 0; alignBytes = 0;
        break;  /* structs and unions will get bigger
                    when we add members to them */
    }

    return TypeAdd(pc, parser, parentType, base, arraySize, identifier, sizeNeed,
        alignBytes);
}

/* stack space used by a Value */
int Type::TypeStackSizeValue(Value *val) {
    if (val != NULL && val->valOnStack)
        return TypeSizeValue(val, false);
    else
        return 0;
}

/* memory used by a Value */
int Type::TypeSizeValue(Value *val, int compact) {
    if (IS_INTEGER_NUMERIC(val) && !compact)
        return sizeof(ALIGN_TYPE);  /* allow some extra room for type extension */
    else if (val->typ->base != kTypeArray)
        return val->typ->sizeNeed;
    else
        return val->typ->fromType->sizeNeed * val->typ->arraySize;
}

/* memory used by a variable given its type and array size */
int Type::TypeSize(ValueType *typ, int arraySize, int compact) {
    if (IS_INTEGER_NUMERIC_TYPE(typ) && !compact)
        return sizeof(ALIGN_TYPE);  /* allow some extra room for type extension */
    else if (typ->base != kTypeArray)
        return typ->sizeNeed;
    else
        return typ->fromType->sizeNeed * arraySize;
}

/* add a base type */
void Type::TypeAddBaseType(Interpreter *pc, ValueType *typeNode, BaseType base,
            int sizeNeed, int alignBytes) {
    typeNode->base = base;
    typeNode->arraySize = 0;
    typeNode->sizeNeed = sizeNeed;
    typeNode->alignBytes = alignBytes;
    typeNode->identifier = pc->strEmpty;
    typeNode->members = NULL;
    typeNode->fromType = NULL;
    typeNode->derivedTypeList = NULL;
    typeNode->onHeap = false;
    typeNode->next = pc->uberType.derivedTypeList;
    pc->uberType.derivedTypeList = typeNode;
}

/* initialize the type system */
void Type::TypeInit(Interpreter *pc) {
    struct IntAlign {char x; int y;} ia;
    struct ShortAlign {char x; short y;} sa;
    struct CharAlign {char x; char y;} ca;
    struct LongAlign {char x; long y;} la;
    struct DoubleAlign {char x; double y;} da;
    struct PointerAlign {char x; void *y;} pa;

    intAlignBytes = (char*)&ia.y - &ia.x;
    pointerAlignBytes = (char*)&pa.y - &pa.x;

    pc->uberType.derivedTypeList = NULL;
    TypeAddBaseType(pc, &pc->intType, kTypeInt, sizeof(int), intAlignBytes);
    TypeAddBaseType(pc, &pc->shortType, kTypeShort, sizeof(short),
        (char*)&sa.y - &sa.x);
    TypeAddBaseType(pc, &pc->charType, kTypeChar, sizeof(char),
        (char*)&ca.y - &ca.x);
    TypeAddBaseType(pc, &pc->longType, kTypeLong, sizeof(long),
        (char*)&la.y - &la.x);
    TypeAddBaseType(pc, &pc->unsignedIntType, kTypeUnsignedInt,
        sizeof(unsigned int), intAlignBytes);
    TypeAddBaseType(pc, &pc->unsignedShortType, kTypeUnsignedShort,
        sizeof(unsigned short), (char*)&sa.y - &sa.x);
    TypeAddBaseType(pc, &pc->unsignedLongType, kTypeUnsignedLong,
        sizeof(unsigned long), (char*)&la.y - &la.x);
    TypeAddBaseType(pc, &pc->unsignedCharType, kTypeUnsignedChar,
        sizeof(unsigned char), (char*)&ca.y - &ca.x);
    TypeAddBaseType(pc, &pc->voidType, kTypeVoid, 0, 1);
    TypeAddBaseType(pc, &pc->functionType, kTypeFunction, sizeof(int),
        intAlignBytes);
    TypeAddBaseType(pc, &pc->macroType, kTypeMacro, sizeof(int), intAlignBytes);
    TypeAddBaseType(pc, &pc->gotoLabelType, kTypeGotoLabel, 0, 1);
    TypeAddBaseType(pc, &pc->fPType, kTypeFP, sizeof(double),
        (char*)&da.y - &da.x);
    TypeAddBaseType(pc, &pc->typeType, kTypeType, sizeof(double),
    (char*)&da.y - &da.x);  /* must be large enough to cast to a double */
    pc->charArrayType = TypeAdd(pc, NULL, &pc->charType, kTypeArray, 0,
        pc->strEmpty, sizeof(char), (char*)&ca.y - &ca.x);
    pc->charPtrType = TypeAdd(pc, NULL, &pc->charType, kTypePointer, 0,
        pc->strEmpty, sizeof(void*), pointerAlignBytes);
    pc->charPtrPtrType = TypeAdd(pc, NULL, pc->charPtrType, kTypePointer, 0,
        pc->strEmpty, sizeof(void*), pointerAlignBytes);
    pc->voidPtrType = TypeAdd(pc, NULL, &pc->voidType, kTypePointer, 0,
        pc->strEmpty, sizeof(void*), pointerAlignBytes);
}

/* deallocate heap-allocated types */
void Type::TypeCleanupNode(Interpreter *pc, ValueType *typ){
    ValueType *subType;
    ValueType *nextSubType;

    /* clean up and free all the sub-nodes */
    for (subType = typ->derivedTypeList; subType != NULL;
            subType = nextSubType) {
        nextSubType = subType->next;
        TypeCleanupNode(pc, subType);
        if (subType->onHeap) {
            /* if it's a struct or union deallocate all the member values */
            if (subType->members != NULL) {
                (pc, subType->members);
                Interpreter::HeapFreeMem(subType->members);
            }

            /* free this node */
            Interpreter::HeapFreeMem(subType);
        }
    }
}

void Type::TypeCleanup(Interpreter *pc) {
    TypeCleanupNode(pc, &pc->uberType);
}

/* parse a struct or union declaration */
void Type::TypeParseStruct(ParseState *parser, ValueType **typ,
    int isStruct) {
    char *memberIdentifier;
    char *structIdentifier;
    LexToken token;
    int alignBoundary;
    Value *memberValue;
    Interpreter *pc = parser->pc;
    Value *lexValue;
    ValueType *memberType;

    token = Lex::LexGetToken(parser, &lexValue, false);
    if (token == kTokenIdentifier) {
        Lex::LexGetToken(parser, &lexValue, true);
        structIdentifier = lexValue->val->identifier;
        token = Lex::LexGetToken(parser, NULL, false);
    } else {
        static char tempNameBuf[7] = "^s0000";
        structIdentifier = Platform::PlatformMakeTempName(pc, tempNameBuf);
    }

    *typ = TypeGetMatching(pc, parser, &parser->pc->uberType,
        isStruct ? kTypeStruct : kTypeUnion, 0, structIdentifier, true);
    if (token == kTokenLeftBrace && (*typ)->members != NULL)
        Platform::ProgramFail(parser, "data type '%t' is already defined", *typ);

    token = Lex::LexGetToken(parser, NULL, false);
    if (token != kTokenLeftBrace) {
        /* use the already defined structure */
#if 0
        if ((*typ)->members == NULL)
            Platform::ProgramFail(parser, "structure '%s' isn't defined",
                lexValue->val->identifier);
#endif
        return;
    }

    if (pc->topStackFrame != NULL)
        Platform::ProgramFail(parser, "struct/union definitions can only be globals");

    Lex::LexGetToken(parser, NULL, true);
    (*typ)->members = (Table *)(pc, parser,
        sizeof(struct Table)+STRUCT_TABLE_SIZE*sizeof(struct TableEntry), true);
    (*typ)->members->hashTable =
        (struct TableEntry**)((char*)(*typ)->members + sizeof(struct Table));
    MyTable::TableInitTable((*typ)->members,
        (struct TableEntry**)((char*)(*typ)->members + sizeof(struct Table)),
        STRUCT_TABLE_SIZE, true);

    do {
        TypeParse(pc, parser, &memberType, &memberIdentifier, NULL);
        if (memberType == NULL || memberIdentifier == NULL)
            Platform::ProgramFail(parser, "invalid type in struct");

        memberValue = Variable::VariableAllocValueAndData(pc, parser, sizeof(int), false,
            NULL, true);
        memberValue->typ = memberType;
        if (isStruct) {
            /* allocate this member's location in the struct */
            alignBoundary = memberValue->typ->alignBytes;
            if (((*typ)->sizeNeed & (alignBoundary-1)) != 0)
                (*typ)->sizeNeed +=
                    alignBoundary - ((*typ)->sizeNeed & (alignBoundary-1));

            memberValue->val->integer = (*typ)->sizeNeed;
            (*typ)->sizeNeed += TypeSizeValue(memberValue, true);
        } else {
            /* union members always start at 0, make sure it's big enough
                to hold the largest member */
            memberValue->val->integer = 0;
            if (memberValue->typ->sizeNeed > (*typ)->sizeNeed)
                (*typ)->sizeNeed = TypeSizeValue(memberValue, true);
        }

        /* make sure to align to the size of the largest member's alignment */
        if ((*typ)->alignBytes < memberValue->typ->alignBytes)
            (*typ)->alignBytes = memberValue->typ->alignBytes;

        /* define it */
        if (!MyTable::TableSet(pc, (*typ)->members, memberIdentifier, memberValue,
                parser->fileName, parser->line, parser->characterPos))
            Platform::ProgramFail(parser, "member '%s' already defined", &memberIdentifier);

        if (Lex::LexGetToken(parser, NULL, true) != kTokenSemicolon)
            Platform::ProgramFail(parser, "semicolon expected");

    } while (Lex::LexGetToken(parser, NULL, false) != kTokenRightBrace);

    /* now align the structure to the size of its largest member's alignment */
    alignBoundary = (*typ)->alignBytes;
    if (((*typ)->sizeNeed & (alignBoundary-1)) != 0)
        (*typ)->sizeNeed += alignBoundary - ((*typ)->sizeNeed & (alignBoundary-1));

    Lex::LexGetToken(parser, NULL, true);
}

/* create a system struct which has no user-visible members */
ValueType *Type::TypeCreateOpaqueStruct(Interpreter *pc, ParseState *parser,
    const char *structName, int size) {
    ValueType *typ = TypeGetMatching(pc, parser, &pc->uberType,
        kTypeStruct, 0, structName, false);

    /* create the (empty) table */
    typ->members = (Table *)(pc,
        parser,
        sizeof(struct Table)+STRUCT_TABLE_SIZE*sizeof(struct TableEntry), true);
    typ->members->hashTable = (struct TableEntry**)((char*)typ->members +
        sizeof(struct Table));
    MyTable::TableInitTable(typ->members,
        (struct TableEntry**)((char*)typ->members+sizeof(struct Table)),
        STRUCT_TABLE_SIZE, true);
    typ->sizeNeed = size;

    return typ;
}

/* parse an enum declaration */
void Type::TypeParseEnum(ParseState *parser, ValueType **typ) {
    int enumValue = 0;
    char *enumIdentifier;
    LexToken token;
    Value *lexValue;
    Value initValue;
    Interpreter *pc = parser->pc;

    token = Lex::LexGetToken(parser, &lexValue, false);
    if (token == kTokenIdentifier) {
        Lex::LexGetToken(parser, &lexValue, true);
        enumIdentifier = lexValue->val->identifier;
        token = Lex::LexGetToken(parser, NULL, false);
    } else {
        static char tempNameBuf[7] = "^e0000";
        enumIdentifier = Platform::PlatformMakeTempName(pc, tempNameBuf);
    }

    TypeGetMatching(pc, parser, &pc->uberType, kTypeEnum, 0, enumIdentifier,
        token != kTokenLeftBrace);
    *typ = &pc->intType;
    if (token != kTokenLeftBrace) {
        /* use the already defined enum */
        if ((*typ)->members == NULL)
            Platform::ProgramFail(parser, "enum '%s' isn't defined", enumIdentifier);

        return;
    }

    if (pc->topStackFrame != NULL)
        Platform::ProgramFail(parser, "enum definitions can only be globals");

    Lex::LexGetToken(parser, NULL, true);
    (*typ)->members = &pc->globalTable;
    memset((void*)&initValue, '\0', sizeof(Value));
    initValue.typ = &pc->intType;
    initValue.val = (union AnyValue*)&enumValue;
    do {
        if (Lex::LexGetToken(parser, &lexValue, true) != kTokenIdentifier)
            Platform::ProgramFail(parser, "identifier expected");

        enumIdentifier = lexValue->val->identifier;
        if (Lex::LexGetToken(parser, NULL, false) == kTokenAssign) {
            Lex::LexGetToken(parser, NULL, true);
            enumValue = Expression::ExpressionParseInt(pc, parser);
        }

        (pc, parser, enumIdentifier, &initValue, NULL, false);

        token = Lex::LexGetToken(parser, NULL, true);
        if (token != kTokenComma && token != kTokenRightBrace)
            Platform::ProgramFail(parser, "comma expected");

        enumValue++;
    } while (token == kTokenComma);
}

/* parse a type - just the basic type */
int Type::TypeParseFront(ParseState *parser, ValueType **typ,
    int *isStatic) {
    int unSigned = false;
    int staticQualifier = false;
    LexToken token;
    ParseState before;
    Value *lexerValue;
    Value *varValue;
    Interpreter *pc = parser->pc;
    *typ = NULL;

    /* ignore leading type qualifiers */
    Parse::ParserCopy(&before, parser);
    token = Lex::LexGetToken(parser, &lexerValue, true);
    while (token == kTokenStaticType || token == kTokenAutoType ||
            token == kTokenRegisterType || token == kTokenExternType) {
        if (token == kTokenStaticType)
            staticQualifier = true;

        token = Lex::LexGetToken(parser, &lexerValue, true);
    }

    if (isStatic != NULL)
        *isStatic = staticQualifier;

    /* handle signed/unsigned with no trailing type */
    if (token == kTokenSignedType || token == kTokenUnsignedType) {
        LexToken FollowToken = Lex::LexGetToken(parser, &lexerValue, false);
        unSigned = (token == kTokenUnsignedType);

        if (FollowToken != kTokenIntType && FollowToken != kTokenLongType &&
                FollowToken != kTokenShortType && FollowToken != kTokenCharType) {
            if (token == kTokenUnsignedType)
                *typ = &pc->unsignedIntType;
            else
                *typ = &pc->intType;

            return true;
        }

        token = Lex::LexGetToken(parser, &lexerValue, true);
    }

    switch (token) {
    case kTokenIntType:
        *typ = unSigned ? &pc->unsignedIntType : &pc->intType;
        break;
    case kTokenShortType:
        *typ = unSigned ? &pc->unsignedShortType : &pc->shortType;
        break;
    case kTokenCharType:
        *typ = unSigned ? &pc->unsignedCharType : &pc->charType;
        break;
    case kTokenLongType:
        *typ = unSigned ? &pc->unsignedLongType : &pc->longType;
        break;
    case kTokenFloatType:
    case kTokenDoubleType:
        *typ = &pc->fPType;
        break;
    case kTokenVoidType:
        *typ = &pc->voidType;
        break;
    case kTokenStructType: case kTokenUnionType:
        if (*typ != NULL)
            Platform::ProgramFail(parser, "bad type declaration");
        TypeParseStruct(parser, typ, token == kTokenStructType);
        break;
    case kTokenEnumType:
        if (*typ != NULL)
            Platform::ProgramFail(parser, "bad type declaration");

        TypeParseEnum(parser, typ);
        break;
    case kTokenIdentifier:
        /* we already know it's a typedef-defined type because we got here */
        Variable::VariableGet(pc, parser, lexerValue->val->identifier, &varValue);
        *typ = varValue->val->typ;
        break;

    default:
        Parse::ParserCopy(parser, &before);
        return false;
    }

    return true;
}

/* parse a type - the part at the end after the identifier. eg.
    array specifications etc. */
ValueType *Type::TypeParseBack(Interpreter *pc, ParseState *parser,
    ValueType *fromType) {
    LexToken token;
    ParseState before;

    Parse::ParserCopy(&before, parser);
    token = Lex::LexGetToken(parser, NULL, true);
    if (token == kTokenLeftSquareBracket) {
        /* add another array bound */
        if (Lex::LexGetToken(parser, NULL, false) == kTokenRightSquareBracket) {
            /* an unsized array */
            Lex::LexGetToken(parser, NULL, true);
            return TypeGetMatching(parser->pc, parser,
                TypeParseBack(pc, parser, fromType), kTypeArray, 0,
                    parser->pc->strEmpty, true);
        } else {
            /* get a numeric array size */
            enum RunMode OldMode = parser->mode;
            int arraySize;
            parser->mode = kRunModeRun;
            arraySize = Expression::ExpressionParseInt(pc, parser);
            parser->mode = OldMode;

            if (Lex::LexGetToken(parser, NULL, true) != kTokenRightSquareBracket)
                Platform::ProgramFail(parser, "']' expected");

            return TypeGetMatching(parser->pc, parser,
                TypeParseBack(pc, parser, fromType), kTypeArray, arraySize,
                    parser->pc->strEmpty, true);
        }
    } else {
        /* the type specification has finished */
        Parse::ParserCopy(parser, &before);
        return fromType;
    }
}

/* parse a type - the part which is repeated with each
    identifier in a declaration list */
void Type::TypeParseIdentPart(Interpreter *pc, ParseState *parser, ValueType *basicTyp,
    ValueType **typ, char **identifier) {
    int done = false;
    LexToken token;
    Value *lexValue;
    ParseState before;
    *typ = basicTyp;
    *identifier = parser->pc->strEmpty;

    while (!done) {
        Parse::ParserCopy(&before, parser);
        token = Lex::LexGetToken(parser, &lexValue, true);
        switch (token) {
        case kTokenOpenBracket:
            if (*typ != NULL)
                Platform::ProgramFail(parser, "bad type declaration");

            TypeParse(pc, parser, typ, identifier, NULL);
            if (Lex::LexGetToken(parser, NULL, true) != kTokenCloseBracket)
                Platform::ProgramFail(parser, "')' expected");
            break;

        case kTokenAsterisk:
            if (*typ == NULL)
                Platform::ProgramFail(parser, "bad type declaration");

            *typ = TypeGetMatching(parser->pc, parser, *typ, kTypePointer, 0,
                parser->pc->strEmpty, true);
            break;

        case kTokenIdentifier:
            if (*typ == NULL || *identifier != parser->pc->strEmpty)
                Platform::ProgramFail(parser, "bad type declaration");

            *identifier = lexValue->val->identifier;
            done = true;
            break;

        default: Parse::ParserCopy(parser, &before); done = true; break;
        }
    }

    if (*typ == NULL)
        Platform::ProgramFail(parser, "bad type declaration");

    if (*identifier != parser->pc->strEmpty) {
        /* parse stuff after the identifier */
        *typ = TypeParseBack(pc, parser, *typ);
    }
}

/* parse a type - a complete declaration including identifier */
void Type::TypeParse(Interpreter *pc, ParseState *parser, ValueType **typ,
    char **identifier, int *isStatic) {
    ValueType *basicType;

    TypeParseFront(parser, &basicType, isStatic);
    TypeParseIdentPart(pc, parser, basicType, typ, identifier);
}

/* check if a type has been fully defined - otherwise it's
    just a forward declaration */
int Type::TypeIsForwardDeclared(ParseState *parser, ValueType *typ) {
    if (typ->base == kTypeArray)
        return TypeIsForwardDeclared(parser, typ->fromType);

    if ((typ->base == kTypeStruct || typ->base == kTypeUnion) &&
            typ->members == NULL)
        return true;

    return false;
}
